package com.snow.utils;

import com.github.pagehelper.util.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @Description:
 * 缓存实现类
 * @author: snow
 * @Date: 2020-01-20
 **/
public class MapCache {
    /**
     * 默认存储1024个缓存
     */
    private static final int DEFAULT_CACHES=1024;
    public MapCache(int cacheCount){
        //ConcurrentHashMap是Java中的一个线程安全且高效的HashMap实现。
        // 平时涉及高并发如果要用map结构，那第一时间想到的就是它。
        cachePool = new ConcurrentHashMap<>(cacheCount);
    }

    private MapCache() {
        this(DEFAULT_CACHES);
    }
    private static MapCache INS = new MapCache();
    public static  MapCache single(){
        return INS;
    }
    /**
     * 缓存容器
     */
    private Map<String,CacheObject> cachePool ;

    /**
     * 读取一个缓存器
     * @param key
     * @param <T>
     * @return
     */
    public <T> T get(String key){
        if(StringUtils.isNotBlank(key)){
            CacheObject cacheObject = cachePool.get(key);
            if(cacheObject != null){
                long current = System.currentTimeMillis() / 1000;
                //如果缓存还没过期
                //<=0 永久缓存
                if(cacheObject.getExpired() <= 0 || cacheObject.getExpired() > current){
                    return (T) cacheObject.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 读取一个哈希缓存
     *
     * @param key
     * @param field
     * @param <T>
     * @return
     */
    public <T> T getHashCache(String key,String field){
        key = key + ":" + field;
        return this.get(key);

    }

    /**
     * 设置一个没有过期时间的缓存
     * @param key
     * @param value
     */
    public void set(String key,Object value){
        this.set(key,value,-1);
    }

    /**
     *设置一个带过期时间的缓存
     * @param key
     * @param value
     * @param expired
     */
    public void set(String key,Object value,long expired){
        expired = expired > 0 ? System.currentTimeMillis() / 1000 + expired : expired;
        CacheObject cacheObject = new CacheObject(key, value, expired);
        cachePool.put(key,cacheObject);
    }

    /**
     * 设置一个带过期时间的哈希缓存
     * @param key
     * @param field 缓存field
     * @param value
     * @param expired
     */
    public void setHashCache(String key,String field,Object value,long expired){
        key = key + ":" + field;
        this.set(key,value,expired);

    }

    /**
     * 无过期时间的哈希缓存
     * @param key
     * @param field
     * @param value
     */
    public void setHashCache(String key,String field,Object value){
        this.setHashCache(key,field,value,-1);
    }

    /**
     * 根据key删除缓存
     * @param key
     */
    public void delete(String key){
        cachePool.remove(key);
    }
    public void deleteHashCache(String key,String field){
        key = key + ":" + field;
        this.delete(key);
    }
    public void clear(){
        cachePool.clear();
    }




    /**
     * 1、用static修饰的内部类，称为静态内部类，完全属于外部类本身，不属于外部类某一个对象
     *
     *
     *
     * 2、static关键字的作用是把修饰的成员变成类相关，而不是实例相关
     *
     *
     *
     * 3、静态内部类可以包含静态成员，也可以包含非静态成员，但是在非静态内部类中不可以声明静态成员。
     *
     *
     *
     * 4、静态类内部不可以访问外部类的实例成员，只能访问外部类的类成员，即使是静态内部类的实例方法也不能访问外部类的实例成员，只能访问外部类的静态成员
     *
     *
     *
     * 5、外部类不可以定义为静态类，Java中静态类只有一种，那就是静态内部类，顶级类不能用static 修饰
     *
     * 6、外部类如何调用静态内部类中的属性和方法
     *
     * 　　　1）外部类可以通过创建静态内部类实例的方法来调用静态内部类的非静态属性和方法
     *
     * 　　　2）外部类可以直接通过“ 外部类.内部类.属性（方法）” 的方式直接调用静态内部类中的静态属性和方法
     *
     * 7、如何创建静态内部类实例
     *
     * 　　　1）在非外部类中：外部类名.内部类名 name = new 外部类名.内部类名();
     *
     * 　　　2）在外部类中：内部类名 name = new 内部类名();
     *
     *
     */
    private static class CacheObject{
        private String key;
        private Object value;
        private Long expired;
        public CacheObject(String key,Object value,Long expired){
            this.key = key;
            this.value = value;
            this.expired = expired;
        }
        public String getKey(){
            return key;
        }
        public Object getValue(){
            return value;
        }
        public Long getExpired(){
            return expired;
        }
    }

}
